/*-------------------------------------------------------------------------
 *
 * thread.c
 *
 *		  系统调用的原型和宏，用于帮助使线程库可重入，并可从线程应用程序中安全使用。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 * src/port/thread.c
 *
 *-------------------------------------------------------------------------
 */

#include "c.h"

#include <pwd.h>


/*
 * 线程有时需要特殊命名的函数版本，这些函数在静态缓冲区中返回数据，例如 strerror_r() 而不是 strerror()。其他操作系统内部使用 pthread_setspecific() 和 pthread_getspecific() 来允许标准库函数向多线程应用程序返回静态数据。有些操作系统则没有这两者。
 *
 * 进一步的混淆在于，许多使用 pthread_setspecific/pthread_getspecific() 的操作系统也有 *_r 版本的标准库函数，以确保与需要它们的操作系统的兼容性。然而，从内部来看，这些 *_r 函数仅仅调用线程安全的标准库函数。
 *
 * 例如，BSD/OS 4.3 使用 Bind 8.2.3 来实现 getpwuid()。在内部，getpwuid() 调用 pthread_setspecific/pthread_getspecific() 以线程安全的方式返回静态数据给调用者。然而，BSD/OS 也有 getpwuid_r()，它只是调用 getpwuid() 并调整参数以匹配 getpwuid_r() 函数声明。因此，虽然 BSD/OS 具有 getpwuid_r()，但并不是强制的。它也没有 strerror_r()，所以我们不能仅依赖 *_r 函数用于多线程程序。
 *
 * 目前的设置是按以下顺序尝试线程：
 *
 *    使用 *_r 函数名，如果它们存在
 *        (*_THREADSAFE=yes)
 *    使用非 *_r 函数，如果它们是线程安全的
 */

#ifndef WIN32

/*
 * 包装器 around getpwuid() 或 getpwuid_r() 以模仿 POSIX getpwuid_r()
 * 行为，如果该函数不可用或不需要。
 *
 * 根据 POSIX，可能的情况有：
 * 成功：返回零，*result 非 NULL
 * 找不到 uid：返回零，*result 为 NULL
 * 查找期间出错：返回错误号，*result 为 NULL
 * （调用者不应假设 errno 变量已设置）
 */
static int fc_pqGetpwuid(uid_t uid, struct passwd *fc_resultbuf, char *fc_buffer,
		   size_t fc_buflen, struct passwd **result)
{
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETPWUID_R)
	return getpwuid_r(uid, fc_resultbuf, fc_buffer, fc_buflen, result);
#else
	/* 没有 getpwuid_r() 可用，只需使用 getpwuid() */
	errno = 0;
	*result = getpwuid(uid);
	/* 健全性：确保成功时返回零 */
	return (*result == NULL) ? errno : 0;
#endif
}

/*
 * pg_get_user_name - 获取具有给定 ID 用户的名称
 *
 * 成功时，用户名称将返回到缓冲区（大小为 buflen），并返回“true”。失败时，带有本地化的错误消息将返回到缓冲区，并返回“false”。
 */
bool pg_get_user_name(uid_t fc_user_id, char *fc_buffer, size_t fc_buflen)
{
	char		fc_pwdbuf[BUFSIZ];
	struct passwd fc_pwdstr;
	struct passwd *fc_pw = NULL;
	int			fc_pwerr;

	fc_pwerr = fc_pqGetpwuid(fc_user_id, &fc_pwdstr, fc_pwdbuf, sizeof(fc_pwdbuf), &fc_pw);
	if (fc_pw != NULL)
	{
		strlcpy(fc_buffer, fc_pw->pw_name, fc_buflen);
		return true;
	}
	if (fc_pwerr != 0)
		snprintf(fc_buffer, fc_buflen,
				 _("could not look up local user ID %d: %s"),
				 (int) fc_user_id,
				 strerror_r(fc_pwerr, fc_pwdbuf, sizeof(fc_pwdbuf)));
	else
		snprintf(fc_buffer, fc_buflen,
				 _("local user with ID %d does not exist"),
				 (int) fc_user_id);
	return false;
}

/*
 * pg_get_user_home_dir - 获取具有给定 ID 用户的家目录
 *
 * 成功时，目录路径将返回到缓冲区（大小为 buflen），并返回“true”。失败时，带有本地化的错误消息将返回到缓冲区，并返回“false”。
 *
 * 请注意，这并不包含先检查 $HOME 的常见行为，因为它与查询的 user_id 无关。
 */
bool pg_get_user_home_dir(uid_t fc_user_id, char *fc_buffer, size_t fc_buflen)
{
	char		fc_pwdbuf[BUFSIZ];
	struct passwd fc_pwdstr;
	struct passwd *fc_pw = NULL;
	int			fc_pwerr;

	fc_pwerr = fc_pqGetpwuid(fc_user_id, &fc_pwdstr, fc_pwdbuf, sizeof(fc_pwdbuf), &fc_pw);
	if (fc_pw != NULL)
	{
		strlcpy(fc_buffer, fc_pw->pw_dir, fc_buflen);
		return true;
	}
	if (fc_pwerr != 0)
		snprintf(fc_buffer, fc_buflen,
				 _("could not look up local user ID %d: %s"),
				 (int) fc_user_id,
				 strerror_r(fc_pwerr, fc_pwdbuf, sizeof(fc_pwdbuf)));
	else
		snprintf(fc_buffer, fc_buflen,
				 _("local user with ID %d does not exist"),
				 (int) fc_user_id);
	return false;
}

#endif							/* !WIN32 */
